<?php

declare(strict_types=1);

namespace app\common\service\sys;

use app\common\traits\ServiceTrait;
use app\common\service\NodeService;
use app\common\service\ToolsService;
use app\common\dao\sys\MenuDao;
use app\common\dao\sys\UserRoleDao;

/**
 * 系统节点菜单
 * @author:kaige
 * @update:2022-06-12
 * @copyright ©2022 泉州市盛筑信息科技有限公司 版权所有
 */
class MenuService
{
  protected $dao = null;

  public function __construct()
  {
    $this->dao = new MenuDao();
  }
  use ServiceTrait;

  /**
   * 重写 表格过滤条件
   * @param $post
   * @return array
   */
  public function _list_where($post)
  {
    $where = [];
    $where[] = ['menu_id', '<>', ''];
    $where[] = ['p_menu_id', '<>', ''];

    if (isset($post['is_menu']) && $post['is_menu'] == 1) {
      $where[] = ['is_menu', '=', 1];
    }

    $result['where'] = $where;
    $result['field'] = ["is_sys,is_lock,type,menu_type
    ,title,name,node,params,icon,open_type,is_left,width,height,is_max,is_menu
    ,is_auth,is_login,is_home_entry,badge,status,is_extend,sort,create_time,update_time
    ,p_menu_id as parentId,p_menu_id as pid,id as mid,menu_id as id"];
    $result['order'] = ['sort asc,create_time asc'];
    return $result;
  }

  /**
   * 获取所有列表树型--换了id menu_id 注意
   * @return array
   */
  public function menuTree()
  {
    $list = $this->dao->getListAll([
      ['is_menu', '=', 1],
      ['menu_id', '<>', ''],
      ['p_menu_id', '<>', ''],
      ['type', '<>', 'a']
    ], ['sort' => 'asc'], ["name,p_menu_id as pid,id as mid,menu_id as value,menu_id as id"]);
    return \app\common\util\TreeUtil::list_to_tree($list, 0, 'id', 'pid', 'children');
  }

  /**
   * 获取单条菜单记录 
   */
  public function getOneMenu($id = 0, $menu_id = '')
  {
    if ($id > 0) {
      return object_array($this->dao->getInfoById($id));
    }
    if (!empty($menu_id)) {
      return object_array($this->dao->getInfoByWhere([['menu_id', '=', $menu_id]]));
    }
    return [];
  }

  /**
   * 保存菜单
   */
  public function saveMenuData($data)
  {
    if (isset($data['is_lock']) && $data['is_lock'] == 1) {
      $data['is_lock'] = 1;
    } else {
      $data['is_lock'] = 0;
    }

    if (isset($data['is_max']) && $data['is_max'] == 1) {
      $data['is_max'] = 1;
    } else {
      $data['is_max'] = 0;
    }

    if (isset($data['is_home_entry']) && $data['is_home_entry'] == 1) {
      $data['is_home_entry'] = 1;
    } else {
      $data['is_home_entry'] = 2;
    }

    $indata = [
      'menu_id'       => $data['menu_id'],
      'p_menu_id'     => $data['p_menu_id'],
      'icon'          => $data['icon'],
      'params'        => $data['params'],
      'name'          => $data['name'],
      'title'         => $data['title'],
      'open_type'     => $data['open_type'],
      'width'         => $data['width'],
      'height'        => $data['height'],
      'sort'          => $data['sort'],
      'is_lock'       => $data['is_lock'],
      'is_max'        => $data['is_max'],
      'is_home_entry' => $data['is_home_entry'],
      'node'          => $data['node'],
      'badge'         => $data['badge']
    ];
    if (isset($data['id']) && !empty($data['id'])) {
      //当前是修改，就需要判断是否会陷入死循环
      if ($this->checkDie($indata['menu_id'], $indata['p_menu_id']) === false) {
        return _err('上一级填错了吧，会陷入死循环的');
      }
      $result = $this->dao->update($data['id'], $indata);
      if ($result) {
        return _ok(1021);
      }
    }
    return _err(1022, $indata);
  }

  /**
   * 提取左侧菜单
   */
  public function menutypeout($list, $type = 1)
  {
    $tname = ($type == 1 ? 'is_left' : '');
    $tmp = [];
    foreach ($list as $k => $v) {
      if ($v[$tname] == 1) {
        $tmp[] = $v;
      }
    }
    return $tmp;
  }

  /**
   * 预先判断死循环
   * @param string $id   当前id
   * @param string $pid  预挂载的父id
   * @param int $n    循环次数 
   * @return bool     如果为true就是通过了，否则就是未通过
   */
  public function checkDie($id, $pid, $n = 10)
  {
    //设置计数器，防止极端情况下陷入死循环了（其他地方如果设置的有问题死循环的话，这里就报错了）
    if ($n <= 0) {
      return false;
    }
    if ($id == $pid) {
      return false;
    }
    if ($pid == '0') { //顶级
      return true;
    }
    $pinfo = $this->dao->getInfoByWhere(['menu_id' => $pid]);
    if (!$pinfo) {
      return false;
    }
    if ($pinfo['p_menu_id'] == $id) {
      return false;
    }
    $n--;
    return $this->checkDie($id, $pinfo['p_menu_id'], $n);
  }

  /**
   * 返回菜单数据（树型）
   * @param int $user_id 用户ID
   * @param int $type 1=后台菜单用，2=分配权限用
   */
  public function getMenuDataTree($user_id, $type = 1)
  {
    //if (cache('?kaige_u_menu_' . $user_id) && $type == 1) {
    // 如果有缓存，并且类型是1的话，就取缓存
    //   $menus = cache('kaige_u_menu_' . $user_id);
    //} else {
    $list = $this->getRoleMenuData($user_id, $type); // 权限过滤得到菜单记录
    $menuTree = $this->arr2tree($list); // 变为菜单树
    $leftemenu = $this->menutypeout($list, 1);
    //根据字段sort对数组$leftemenu进行升序排列
    $lm_sorts = array_column($leftemenu, 'sort');
    array_multisort($lm_sorts, SORT_ASC, $leftemenu);
    $menus = [
      'menu' => $menuTree,
      'leftmenu' => $leftemenu
    ];
    //存储
    //    if ($type == 1) {
    //        cache('kaige_u_menu_' . $user_id, $menus, 3600);
    //    }
    //}
    return $menus;
  }

  /**
   * 获取用户_所有角色_所有菜单
   * @param int $type 1=后台菜单用，2=分配权限用，判断权限
   */
  public function getRoleMenuData($user_id, $type = 1)
  {
    if ($user_id == config('kaige.super_id') || $user_id == 168 || $user_id == 438) {
      // 超级管理员,取所有
      $where = [];
      if ($type == 1) {
        $where[] = ['is_menu', '=', 1];
      } else {
        $where[] = ['is_auth', '=', 1];
      }
      $where[] = ['menu_id', '<>', ''];
      $where[] = ['p_menu_id', '<>', ''];
      $where[] = ['type', '<>', 'm'];
      $where[] = ['status', '=', 1];
      $list = $this->dao->getListAll($where, ['sort' => 'ASC']);
    } else {
      $userRoleDao = new UserRoleDao();
      //取此管理员的所有角色id
      $roles = $userRoleDao->getUserRoleAllId($user_id);
      //到这里就说明用户是定义的管理员，那么就取所有的角色所对应的权限
      $list = $this->dao->getRoleMenuData($roles, $type);
    }
    if (empty($list)) {
      $list = []; //啥权限都没有，含恨而终!
    } else {
      $list = object_array($list);
    }
    return $list;
  }

  /**
   * 一维数据数组生成数据树
   * @param array $list 数据列表
   * @param string $id ID Key
   * @param string $pid 父ID Key
   * @param string $son 定义子数据Key
   * @return array
   */
  public function arr2tree($list, $id = 'menu_id', $pid = 'p_menu_id', $son = 'children')
  {
    list($tree, $map) = [[], []];
    foreach ($list as $item) {
      if (isset($item['node']) && $item['type'] == 'a') {
        $item['url'] = url($item['node']);
      }
      $map[$item[$id]] = $item;
    }
    foreach ($list as $item) {
      if (isset($item[$pid]) && isset($map[$item[$pid]])) {
        ($map[$item[$pid]][$son])[] = &$map[$item[$id]];
      } else {
        $tree[] = &$map[$item[$id]];
      }
    }
    unset($map);
    return $tree;
  }

  /**
   * 菜单数据转为菜单树
   */
  protected function getMenuTree($list, $p_menu_id)
  {
    $data = [];
    foreach ($list as $k => $v) {
      if (trim($v['p_menu_id']) == trim($p_menu_id)) {
        $row = $v;
        //取当前节点的url
        if ($v['type'] == 'a') {
          $row['url'] = url($v['node']);
        } else {
          $row['url'] = ''; // 模块，控制器，无需url
        }
        unset($row['node']);
        unset($row['createtime']);
        unset($row['updatetime']);
        $row['children'] = $this->getMenuTree($list, $v['menu_id']);
        $data[] = $row;
      }
    }
    return $data;
  }

  /**
   * 获取操作名称
   * @return array
   */
  public function getOperationInfo($node)
  {
    $result = _err();
    $where[] = ['node', '=', parseNodeStr($node)];
    $info    = $this->dao->getInfoByWhere($where);
    if (!$info) {
      return _err('查无数据');
    }
    $result['status'] = true;
    $result['data']   = $info->toArray();
    return $result;
  }

  /**
   * 根据传过来的字段构建设置角色的权限树，结构主要是适配Dtree的数据结构
   * @param $userMenu 当前登陆者的权限树
   * @param $nodeList 当前角色的on的权限
   * @param int $level 层级
   * @param int $p_menu_id 父id
   * @return array
   */
  public function setUserRoleMenuDtree($userMenu, $nodeList, $level = 1, $p_menu_id = '10')
  {
    $dtreeData = [];
    foreach ($userMenu as $v) {
      if (in_array($v['id'], $nodeList)) {
        $isChecked = '1';
      } else {
        $isChecked = '0';
      }
      if (isset($v['children']) && $v['children']) {
        $isLast = false;
        $children = $this->setUserRoleMenuDtree($v['children'], $nodeList, $level + 1, $v['menu_id']);
      } else {
        $isLast = true;
        $children = [];
      }
      $dtreeData[] = [
        'id' => $v['menu_id'],
        'title' => $v['name'],
        'last' => $isLast,
        'level' => $level,
        'parentId' => $p_menu_id,
        'checkArr' => [[
          'type' => '0',
          'checked' => $isChecked
        ]],
        'children' => $children,
        'basicData' => $v['id'] // 这个值才是重要的。
      ];
    }
    return $dtreeData;
  }


  /**
   * 获取控制器节点(自动分组、自动遍历控制器)
   * @return array
   */
  public function getNodeGroupList()
  {
    $nodes = ToolsService::arr2table(NodeService::get(), 'node', 'pnode');
    $groups = [];
    foreach ($nodes as $node) {
      $pnode = explode('/', $node['node'])[0];
      if ($node['node'] === $pnode) {
        $groups[$pnode]['node'] = $node;
      }
      $groups[$pnode]['list'][] = $node;
    }
    return ['nodes' => $nodes, 'groups' => $groups];
  }

  /**
   * 根据节点获取对应权限配置
   * @param string $node 权限节点
   * @return array
   */
  public function getInfoByNode($node)
  {
    $return = ['is_menu'  => 0, 'is_auth'  => 0, 'is_login' => 0];
    $info = $this->dao->getInfoByWhere(['node' => $node]);
    if ($info) {
      $info = $info->toArray();
      $return = [
        'is_menu'  => intval(!empty($info['is_menu'])),
        'is_auth'  => intval(!empty($info['is_auth'])),
        'is_login' => empty($info['is_auth']) ? intval(!empty($info['is_login'])) : 1,
      ];
    }
    return $return;
  }

  /**
   * 清理无效的节点记录
   */
  public function clear()
  {
    $nodes = array_keys(NodeService::get());
    // 清除不存在的及is_sys=0的
    if (false != $this->dao->clearNode($nodes)) {
      return _ok('清理无效节点记录成功');
    }
    return _err('清理无效节点失败！');
  }

  /**
   * 获取反常的节点
   */
  public function getabnormal()
  {
    $nodes = array_keys(NodeService::get());
    //取出不存在的
    $nodata = $this->dao->getListAll([['node', 'NOT IN', $nodes]]);
    if ($nodata) {
      $count = count($nodata);
      return _layui($count, $nodata);
    }
    return _layui(0, []);
  }

  /**
   * 保存节点变更
   */
  public function saveNode($post)
  {
    $data = [];
    foreach ($post['list'] as $key => $vo) {
      if (!empty($vo['node'])) {
        $data['node'] = parseNodeStr($vo['node']); //驼峰转下划线规则  xxxxxxxxxxxxxxxxxx-strtolower($vo['node']); //保证小写
        $data[$vo['name']] = $vo['value'];
        // type 自动
        $dataLe = explode('/', $data['node']);
        $dcl  = count($dataLe);
        if ($dcl == 1) {
          $data['type'] = 'm';
        } else if ($dcl == 2) {
          $data['type'] = 'c';
        } else if ($dcl == 3) {
          $data['type'] = 'a';
        }
      }
    }
    if (!empty($data)) {
      if ($this->dao->saveNode($data)) {
        return _ok(1006);
      };
    }
    return _err(1005);
  }

  /**
   * 批量操作 启禁用、保留废弃
   */
  public function checkfun($data)
  {
    if (isset($data['id']) && isset($data['field']) && isset($data['value'])) {
      if (!empty($data['id']) && !empty($data['field'])) {
        return $this->dao->checkfun($data);
      }
    }
    return _err(1032);
  }
}
